import { BiQuadFilter } from './BiQuadFilter';
import type { FilterType } from './types';

interface FrequencyResponsePoint {
  readonly gain: number; // Gain value (dB)
  readonly frequency: number; // Frequency value (Hz)
}

export interface EQFilterData {
  readonly frequency: number; // Center frequency (Hz)
  readonly gain: number; // Gain value (dB)
  readonly q: number; // Quality factor (Q)
  readonly filterType?: FilterType; // Filter type (optional)
}

/**
 * Parametric Equalizer Class
 * Implements multi-band parametric equalizer functionality for frequency domain audio processing
 */
export class ParametricEqualizer {
  private readonly filters: readonly BiQuadFilter[]; // Array of biquad filters
  private _sampleRate: number = 48000.0; // Sample rate, default 48kHz
  private _preampGain: number = 0.0; // Preamp gain

  /**
   * Constructor
   * @param data Array of equalizer filter parameters
   */
  constructor(data: readonly EQFilterData[]) {
    // Create filter array based on input parameters
    this.filters = data.map(
      (item) =>
        new BiQuadFilter({
          fc: item.frequency,
          gain: item.gain,
          q: item.q,
          filterType: item.filterType,
        }),
    );
  }

  /**
   * Get current sample rate
   */
  public get sampleRate(): number {
    return this._sampleRate;
  }

  /**
   * Set new sample rate
   * @param newRate New sample rate value
   */
  public set sampleRate(newRate: number) {
    if (newRate <= 0 || !Number.isFinite(newRate))
      throw new Error('Sample rate must be a positive finite number');

    this._sampleRate = newRate;
    // Update sample rate for all filters
    this.filters.forEach((filter) => {
      filter.fs = newRate;
    });
  }

  /**
   * Get preamp gain
   */
  public get preampGain(): number {
    return this._preampGain;
  }

  /**
   * Set preamp gain
   * @param value Gain value
   */
  public set preampGain(value: number) {
    if (!Number.isFinite(value))
      throw new TypeError('Preamp gain must be a finite number');

    this._preampGain = value;
  }

  /**
   * Get total number of filters
   */
  public get filterCount(): number {
    return this.filters.length;
  }

  /**
   * Get filter at specified index
   * @param index Filter index
   * @returns Corresponding biquad filter instance
   */
  public getFilter(index: number): BiQuadFilter {
    if (!Number.isInteger(index)) throw new Error('Index must be an integer');

    if (index < 0 || index >= this.filters.length)
      throw new Error(
        `Index out of bounds: ${index}. Valid range: 0-${this.filters.length - 1}`,
      );

    return this.filters[index];
  }

  /**
   * Calculate total gain at specified frequency
   * @param frequency Target frequency point
   * @returns Sum of all filter gains at the frequency point plus preamp gain
   */
  private calculateTotalGain(frequency: number): number {
    const totalGain = this.filters.reduce(
      (sum, filter) => sum + filter.calcDbGain(frequency),
      0,
    );

    return totalGain + this.preampGain;
  }

  /**
   * Calculate equalizer frequency response at specified frequency points
   * @param frequencies Array of frequency points to calculate
   * @returns Array of frequency response points, containing gain values for each frequency point
   */
  public calculateFrequencyResponse(
    frequencies: readonly number[],
  ): FrequencyResponsePoint[] {
    if (!frequencies.length) return [];

    if (!frequencies.every((f) => f > 0 && Number.isFinite(f)))
      throw new Error('All frequencies must be positive finite numbers');

    return frequencies.map((frequency) => ({
      frequency,
      gain: this.calculateTotalGain(frequency),
    }));
  }
}
